package org.lzfto.flink.demo.init;

import java.io.IOException;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.sql.DataSource;

import org.lzfto.flink.demo.common.data.PagingOperation;
import org.lzfto.flink.demo.common.result.PubResult;
import org.lzfto.flink.demo.config.AppConfiguration;
import org.lzfto.flink.demo.config.MyAppProperties;
import org.lzfto.flink.demo.config.MyAppProperties.DataSourceProperties;
import org.lzfto.flink.demo.config.MyAppProperties.HikariProperties;
import org.lzfto.flink.demo.config.mqtt.MQTTConfig;
import org.lzfto.flink.demo.config.websocket.MyWebSocketHandler;
import org.lzfto.flink.demo.config.websocket.WsSession;
import org.lzfto.flink.demo.system.config.Account;
import org.lzfto.flink.demo.utils.SecurityUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.socket.WebSocketSession;

import com.zaxxer.hikari.HikariDataSource;

@Component
public class InitService implements ApplicationListener<ApplicationReadyEvent> {
	
	private final  Logger LOGGER=LoggerFactory.getLogger(getClass());
	
	@Autowired
	private MyAppProperties myAppProperties;
	
	private ConfigurableApplicationContext context;
	
	@Autowired
	private  MyAppProperties prop;
	@Override
	public void onApplicationEvent(ApplicationReadyEvent event) {
		this.removeOldJdbcDriver();
		this.context=event.getApplicationContext();
		AppConfiguration.init(myAppProperties);
		this.initWebSocketTask();
	}
	
	/**
	 * 创建定时任务，每5秒执行一次，检验一些超时没有接收到客户端消息的wssocket会话
	 */
	private void initWebSocketTask() {
		// 创建定时任务，每5秒执行一次
		ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
		executor.scheduleAtFixedRate(() -> {
			ConcurrentHashMap<String, WsSession> sessions = MyWebSocketHandler.getWsSessions();
			//LOGGER.info("当前共有会话{}个",sessions.size());
			int timeout=myAppProperties.getWebSocketTimeout();
			// 使用迭代器安全遍历
			Iterator<Map.Entry<String, WsSession>> iterator = sessions.entrySet().iterator();
			while (iterator.hasNext()) {
				Map.Entry<String, WsSession> entry = iterator.next();
				WsSession value = entry.getValue();
				WebSocketSession session = value.getSession();
				if (System.currentTimeMillis() - value.getConnectTime().getTime() > timeout) {
					try {
						if (session.isOpen()) {
							session.close();
							LOGGER.info("websocket 会话(id={})超时{}秒,已关闭", entry.getKey(),timeout/1000);
						}
						iterator.remove(); // 安全移除
					} catch (IOException e) {
						LOGGER.error("关闭websocket会话失败", e);
					}
				}
			}
		}, 0, 5, TimeUnit.SECONDS); // 初始延迟0秒，之后每5秒执行一次
	}

	private void removeOldJdbcDriver() {
		// 删除旧的JDBC驱动
		// 例如，使用JDBC驱动的类加载器卸载旧的驱动程序

		final String[] driverClasses= new String[] {
			"com.mysql.jdbc.Driver"
		};
		Enumeration<Driver> drivers = DriverManager.getDrivers();
		while (drivers.hasMoreElements()) {
			Driver driver = drivers.nextElement();
			for (String driverClass : driverClasses) {
				if (driver.getClass().getName().equals(driverClass)) {
                    try {
                        DriverManager.deregisterDriver(driver);
						System.out.println("Deregister driver: " + driverClass);
                    } catch (SQLException e) {
                        throw new RuntimeException(e);
                    }
                }
			}
		}
	}
	
	/**
	 * 注册阿耳忒弥斯
	 */
	public PubResult<String> registerArtermis() {
		AppConfiguration config=AppConfiguration.getConf();
		
		if (config.getIsArtemisRegistered()) {
			LOGGER.warn("不能重复注册");
			return PubResult.error("Artermis不要反复注册");
		}
		DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
		MQTTConfig mqttConfig=new MQTTConfig(beanFactory,config);
		mqttConfig.init();
		
		LOGGER.info("注册工厂:{}","mqttPahoClientFactory");
		LOGGER.info("注册通道:{}","mqttOutboundChannel");
		LOGGER.info("注册消息处理器:{}","mqttOutboundHandler");
		return PubResult.ok("注册阿耳忒弥斯成功");
	}
	
	
	public PubResult<String> registerDatasource(String desPassword) {
		AppConfiguration appConfig=AppConfiguration.getConf();
		if (!appConfig.getIsDatasourceRegistered()) {
			try {
				Account account=SecurityUtil.getAccount(desPassword);
				appConfig.setDbUserName(account.getDbUser());
				appConfig.setDbPassword(account.getDbPassword());
				//这个注册过程过程可能需要一定时间
				return this.registerDatasource();
			}
			catch(Exception e) {
				return PubResult.error("登录失败，因为无法注册数据源");
			}
		}
		else {
			return PubResult.ok_data("已经注册过数据源");
		}
	}
	/**
	 * 注册数据源
	 */
	public PubResult<String> registerDatasource() {
        AppConfiguration config=AppConfiguration.getConf();
		if (config.getIsDatasourceRegistered()) {
			LOGGER.warn("不能重复注册");
			return PubResult.ok("数据源已经注册过");
		}
		
		DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) context.getAutowireCapableBeanFactory();
		
		LOGGER.info("注册数据源:{}","dataSource");
		DataSource ds=this.createDataSource();
		beanFactory.registerSingleton("dataSource", ds);
		
		LOGGER.info("注册JDBCTP:{}","JdbcTemplate");
		JdbcTemplate jdbcTp=this.createJdbcTp(ds);
		beanFactory.registerSingleton("JdbcTemplate", jdbcTp);
		
		LOGGER.info("注册命名JDBCTP:{}","namedParameterJdbcTemplate");
		beanFactory.registerSingleton("namedParameterJdbcTemplate",this.createNamedParameterJdbcTp(ds));
		
		PagingOperation.setJdbcTemplate(jdbcTp);
		
		config.setIsDatasourceRegistered(true);
		
		return PubResult.ok("注册数据源成功");
		
	}
	
	private DataSource createDataSource() {
		AppConfiguration config=AppConfiguration.getConf();
		DataSourceProperties dsProp= prop.getDatasource();
		HikariProperties hkProp=  dsProp.getHikari();
		HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(dsProp.getUrl());
        dataSource.setUsername(config.getDbUserName());
        dataSource.setPassword(config.getDbPassword());
        dataSource.setDriverClassName(dsProp.getDriverClassName());
        dataSource.setMaximumPoolSize(hkProp.getMaximumPoolSize()); // 设置最大连接数
        dataSource.setMinimumIdle(hkProp.getMinimumIdle()); // 设置最小空闲连接数
        dataSource.setIdleTimeout(hkProp.getIdleTimeout()); // 设置空闲超时时间
        dataSource.setConnectionTimeout(hkProp.getConnectionTimeout()); // 设置连接超时时间
        dataSource.setAutoCommit(hkProp.isAutoCommit());
        dataSource.setMaxLifetime(hkProp.getMaxLifetime());
        dataSource.setConnectionTestQuery(hkProp.getConnectionTestQuery());
        return dataSource;
    }
	
	private JdbcTemplate createJdbcTp(DataSource ds) {
		return new JdbcTemplate(ds);
	}
	
	private NamedParameterJdbcTemplate createNamedParameterJdbcTp(DataSource ds) {
		return new NamedParameterJdbcTemplate(ds);
	}


	
	
	
}
